在 ASP.NET 使用 Autofac 實作 DI
TLDR
- Autofac 是 ASP.NET 生態系中功能強大的依賴注入(DI)套件,支援多種框架整合。
- 核心註冊方式為
RegisterType<T>().As<I>(),並可透過RegisterAssemblyTypes進行大量自動註冊。 InstancePerLifetimeScope是 Web 應用中最常用的生命週期,對應 ASP.NET Core 的 Scoped。- 當框架不支援 Constructor Injection(如 Web Form/Web Service)或存在循環依賴時,需使用
PropertiesAutowired進行屬性注入。 - 在 MVC/Web API 中,需透過
DependencyResolver或DependencyResolver進行整合,並確保 Controller 被正確註冊。 - 針對
Web.config的設定,可透過自定義Module封裝AppSettings並注入至 Options 類別中,解決型別轉換與單元測試問題。
Autofac 基礎概念與註冊
Autofac 透過 ContainerBuilder 建立容器,並在每個 Request 建立 Lifetime Scope。
型別註冊與自動化
- 基本註冊:使用
RegisterType<{Instance Type}>().As({Declare Type})。 - 大量註冊:使用 Reflection 掃描 Assembly。
csharp
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(x => typeof(IAppService).IsAssignableFrom(x));- 自定義建立方法:當需要手動控制物件實例化時使用:
csharp
builder.Register(c => new TypeA(c.Resolve<TypeB>()));註冊型別對應表
| Method | 描述 |
|---|---|
As() | 註冊型別給指定型別使用 |
AsImplementedInterfaces() | 註冊給自身實作的 Interface 使用(不含 IDisposable) |
AsClosedTypesOf(open) | 註冊給可分配給開放泛型型別的封閉實例使用 |
AsSelf() | 將型別註冊給自身使用 |
TIP
若未設定 As(),預設為 AsSelf()。若已指定其他介面,則不會自動包含 AsSelf(),需手動補上。
Instance Scope 管理
| Instance Scope | 描述 | .NET Core 對應 |
|---|---|---|
InstancePerDependency | 每次呼叫產生新 Instance(預設值) | Transient |
InstancePerLifetimeScope | 每個 Scope 產生一個 Instance | Scoped |
SingleInstance | 整個 Container 共用一個 Instance | Singleton |
進階應用:屬性注入與循環依賴
什麼情況下會遇到這個問題:當框架本身不支援 Constructor Injection,或發生類別間互相依賴(循環依賴)時。
csharp
builder.RegisterType<Main>()
.InstancePerLifetimeScope();
builder.RegisterType<Sub>()
.InstancePerLifetimeScope()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);WARNING
- 循環依賴的型別註冊不可使用
InstancePerDependency()。 - 若無循環依賴需求,請勿傳入
PropertyWiringOptions.AllowCircularDependencies。
各框架整合實作
ASP.NET MVC
需安裝 Autofac.Mvc5。在 Global.asax.cs 中註冊 Controller 並設定 DependencyResolver:
csharp
builder.RegisterControllers(typeof(MvcApplication).Assembly);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));ASP.NET Web API
需安裝 Autofac.WebApi2。在 Global.asax.cs 中設定:
csharp
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);Web Form 與 Web Service
由於不支援 Constructor Injection,必須使用 Property Injection。
- Web Form:透過
Autofac.Integration.Web模組在Web.config設定PropertyInjectionModule。 - Web Service:需實作
WebServiceBase並在建構式中手動呼叫InjectProperties(this)。
封裝 AppSettings 注入
為解決 WebConfigurationManager 靜態呼叫與型別轉換問題,建議建立 OptionsModule:
csharp
private void RegisterOptions<T>(ContainerBuilder builder) where T : class {
string optionsName = typeof(T).Name.Replace("Options", "");
var registrationBuilder = builder.RegisterType<T>().AsSelf().InstancePerLifetimeScope();
foreach (string key in WebConfigurationManager.AppSettings.AllKeys.Where(x => x.StartsWith(optionsName))) {
registrationBuilder.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.Name.Equals(Regex.Replace(key, $@"^{optionsName}:", "", RegexOptions.IgnoreCase), StringComparison.OrdinalIgnoreCase),
(pi, ctx) => Convert.ChangeType(WebConfigurationManager.AppSettings[key], pi.ParameterType)));
}
}異動歷程
- 初版文件建立。